package com.rm.zeyou.service.impl;

import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.rm.zeyou.constant.CommonConstants;
import com.rm.zeyou.constant.NumberConstants;
import com.rm.zeyou.constant.RedisConstants;
import com.rm.zeyou.entity.*;
import com.rm.zeyou.exception.ErrorCodeException;
import com.rm.zeyou.exception.ErrorCodeExceptionEnum;
import com.rm.zeyou.mapper.*;
import com.rm.zeyou.pay.wxpay.entity.GetAccessTokenByCodeBean;
import com.rm.zeyou.pay.wxpay.entity.GetUserInfoBean;
import com.rm.zeyou.pay.wxpay.service.WechatAppLoginService;
import com.rm.zeyou.response.ApiRestResponse;
import com.rm.zeyou.service.TechnicianService;
import com.rm.zeyou.service.UserService;
import com.rm.zeyou.util.address.AddressUtil;
import com.rm.zeyou.util.address.WSSsdrAddress;
import com.rm.zeyou.util.check.CheckEmptyUtil;
import com.rm.zeyou.util.distance.MathDistanceUtil;
import com.rm.zeyou.util.gaode.GaoDeUtil;
import com.rm.zeyou.util.login.TechnicianLoginUtil;
import com.rm.zeyou.util.md5.MD5Utils;
import com.rm.zeyou.util.redis.RedisUtil;
import com.rm.zeyou.vo.request.*;
import com.rm.zeyou.vo.response.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 技师表 服务实现类
 * </p>
 *
 * @author YanYang
 * @since 2025-03-12 15:55:09
 */
@Slf4j
@Service
public class TechnicianServiceImpl extends ServiceImpl<TechnicianMapper, Technician> implements TechnicianService {
    @Resource
    private TechnicianMapper technicianMapper;
    @Resource
    private UserAppraiseMapper userAppraiseMapper;
    @Resource
    private UserAttentionMapper userAttentionMapper;
    @Resource
    private TechnicianTimeMapper technicianTimeMapper;
    @Resource
    private GaoDeUtil gaoDeUtil;
    @Resource
    private TechnicianItemOrderMapper technicianItemOrderMapper;
    @Resource
    private MerchantMapper merchantMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private ItemMapper itemMapper;
    @Resource
    private TechnicianTypeMapper technicianTypeMapper;
    @Resource
    private TechnicianAuthenticationMapper technicianAuthenticationMapper;
    @Resource
    private TechnicianLoginUtil technicianLoginUtil;
    @Resource
    private UserService userService;
    @Resource
    private TechnicianShieldMapper technicianShieldMapper;
    @Resource
    private TechnicianConsumeDetailMapper technicianConsumeDetailMapper;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private TechnicianAvatarAuditMapper technicianAvatarAuditMapper;
    @Resource
    private TechnicianLifePhotoAuditMapper technicianLifePhotoAuditMapper;
    @Resource
    private BrokerApplyMapper brokerApplyMapper;
    @Resource
    private GradeConfigMapper gradeConfigMapper;
    @Resource
    private WechatAppLoginService wechatAppLoginService;
    @Resource
    private CityFareConfigMapper cityFareConfigMapper;
    @Resource
    private CityFareGlobalConfigMapper cityFareGlobalConfigMapper;
    @Resource
    private ApplicationConfigMapper applicationConfigMapper;


    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ApiRestResponse<Map<String, String>> technicianPassLogin(TechnicianPassLoginVo loginVo) {
        Technician technician = technicianMapper.selectOne(new LambdaQueryWrapper<Technician>()
                .eq(Technician::getTechnicianTel, loginVo.getTel()));
        if (CheckEmptyUtil.isEmpty(technician)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.TECHNICIAN_EMPTY);
        }
        if (!technician.getTechnicianPass().equals(MD5Utils.MD5(loginVo.getPassword()))) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.PASS_ERROR);
        }
        Map<String, String> token = technicianLoginUtil.getToken(technician.getId());
        token.put("tid", technician.getId().toString());
        if (CheckEmptyUtil.isEmpty(technician.getOpenId())) {

            token.put("isBindWx", "0");
        } else {
            token.put("isBindWx", "1");
        }

        if (CheckEmptyUtil.isEmpty(technician.getH5OpenId())) {

            token.put("isBindH5Wx", "0");
        } else {
            token.put("isBindH5Wx", "1");
        }
        return ApiRestResponse.success(token);
    }

    @Override
    public ApiRestResponse technicianH5BindWx(Technician technician) throws UnsupportedEncodingException {
        String url = getUserUathUrl(technician.getId(), "https://www.zeyouinlife.com/api/wechat/wechatNotify");
        return ApiRestResponse.success(url);
    }

    /**
     * description 1.获取用户的临时code
     * param [appid, redirectUrl]
     * return java.lang.String
     * authorlgb
     * createTime 2021/8/27 17:30
     **/
    public String getUserUathUrl(Long tid, String redirectUrl) throws UnsupportedEncodingException {
        String accountAppid = applicationConfigMapper.selectOneData("account_appid");
        StringBuffer getcodeUrl = new StringBuffer()
                .append("https://open.weixin.qq.com/connect/oauth2/authorize")
                .append("?appid=" + accountAppid)
                .append("&redirect_uri=" + URLEncoder.encode(redirectUrl, "utf-8"))
                .append("&response_type=code")
                .append("&scope=snsapi_userinfo")
                .append("&state=" + tid)
                .append("#wechat_redirect");

        return getcodeUrl.toString();
    }


    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ApiRestResponse technicianAPPBindWx(Technician technician, UserBindTelVo bindTelVo) {
        GetAccessTokenByCodeBean accessToken = wechatAppLoginService.getAccessToken(bindTelVo.getCode(), 2);
        log.info("accessToken:[{}]", accessToken);
        GetUserInfoBean userInfo = wechatAppLoginService.getUserInfo(accessToken.getOpenid(), accessToken.getAccessToken());
        log.info("userInfo:[{}]", userInfo);
        if (CheckEmptyUtil.isEmpty(userInfo)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.WX_LOGIN_ERROR);
        }
        technician.setOpenId(userInfo.getOpenid());
        technician.setTechnicianAvatar(userInfo.getHeadimgurl());
        technician.setUnionid(userInfo.getUnionid());

        return ApiRestResponse.success(technicianMapper.updateById(technician));

    }

    @Override
    public ApiRestResponse<Void> technicianUpdatePasVerify(TechnicianReplacePassVo replaceVo) {
        userService.checkCode(replaceVo.getTel(), replaceVo.getCode(), CommonConstants.TECHNICIAN_REPLACE_PASS);
        return ApiRestResponse.success();
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ApiRestResponse<Integer> technicianReplacePass(TechnicianReplacePassVo replaceVo) {
        Technician technician = technicianMapper.selectOne(new LambdaQueryWrapper<Technician>()
                .eq(Technician::getTechnicianTel, replaceVo.getTel()));
        if (CheckEmptyUtil.isEmpty(technician)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.TECHNICIAN_EMPTY);
        }
        if (!replaceVo.getPass().equals(replaceVo.getRePass())) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.PASSWORD_INCONSISTENCY);
        }
        if (replaceVo.getPass().length() != 6) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.PASSWORD_LENGTH_ERROR);
        }

        technician.setTechnicianPass(MD5Utils.MD5(replaceVo.getPass()));
        int update = technicianMapper.updateById(technician);
        return ApiRestResponse.success(update);
    }

    @Override
    public ApiRestResponse<TechnicianPersonalCenterResponseVo> technicianPersonalCenter(Technician technician) {
        TechnicianPersonalCenterResponseVo responseVo = technicianConsumeDetailMapper.selectAmountById(technician.getId(), NumberConstants.ONE_CONSTANTS);
        if (CheckEmptyUtil.isEmpty(responseVo)) {
            responseVo = new TechnicianPersonalCenterResponseVo();
            responseVo.setServeCount(NumberConstants.ZERO_CONSTANTS);
            responseVo.setTodayCarAmount(BigDecimal.ZERO);
            responseVo.setTodayItemAmount(BigDecimal.ZERO);
            responseVo.setTodayTransactionAmount(BigDecimal.ZERO);
        }
        if (technician.getTechnicianIdentity().equals(NumberConstants.ONE_CONSTANTS)) {
            responseVo.setTechnicianIdentity(NumberConstants.ONE_CONSTANTS);
        } else {
            int value = brokerApplyMapper.selectCount(new LambdaQueryWrapper<BrokerApply>()
                    .eq(BrokerApply::getTid, technician.getId())
                    .eq(BrokerApply::getAuditStatus, NumberConstants.ZERO_CONSTANTS)).intValue();
            if (value > 0) {
                responseVo.setTechnicianIdentity(NumberConstants.TWO_CONSTANTS);
            } else {
                responseVo.setTechnicianIdentity(NumberConstants.ZERO_CONSTANTS);
            }
        }

        int value = technicianAvatarAuditMapper.selectCount(new LambdaQueryWrapper<TechnicianAvatarAudit>()
                .eq(TechnicianAvatarAudit::getTid, technician.getId())
                .eq(TechnicianAvatarAudit::getAuditStatus, NumberConstants.ZERO_CONSTANTS)).intValue();
        if (value > 0) {
            responseVo.setAvatarAuditStatus(NumberConstants.ONE_CONSTANTS);
        } else {
            responseVo.setAvatarAuditStatus(NumberConstants.ZERO_CONSTANTS);
        }
        int lifeStatus = technicianLifePhotoAuditMapper.selectCount(new LambdaQueryWrapper<TechnicianLifePhotoAudit>()
                .eq(TechnicianLifePhotoAudit::getTid, technician.getId())
                .eq(TechnicianLifePhotoAudit::getAuditStatus, NumberConstants.ZERO_CONSTANTS)).intValue();
        if (lifeStatus > 0) {
            responseVo.setLiferAuditStatus(NumberConstants.ONE_CONSTANTS);
        } else {
            responseVo.setLiferAuditStatus(NumberConstants.ZERO_CONSTANTS);
        }

        responseVo.setTechnicianAvatar(technician.getTechnicianAvatar());
        responseVo.setLifePhoto(technician.getLifePhoto());
        responseVo.setTechnicianName(technician.getTechnicianName());
        responseVo.setTechnicianTel(technician.getTechnicianTel());
        responseVo.setIsOnline(technician.getIsOnline());
        responseVo.setTechnicianIntro(technician.getTechnicianIntro());
        responseVo.setTechnicianSex(technician.getTechnicianSex());
        responseVo.setServiceCity(technician.getServiceCity());
        responseVo.setTechnicianEthnic(technician.getTechnicianEthnic());
        responseVo.setTechnicianHeight(technician.getTechnicianHeight());
        responseVo.setTechnicianWeight(technician.getTechnicianWeight());

        GradeConfig gradeConfig = gradeConfigMapper.selectById(technician.getGradeId());
        responseVo.setGradeName(CheckEmptyUtil.isEmpty(gradeConfig) ? "" : gradeConfig.getGradeName());
        return ApiRestResponse.success(responseVo);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ApiRestResponse<Integer> technicianOnline(Technician technician) {
        if (technician.getIsOnline().equals(NumberConstants.ZERO_CONSTANTS)) {
            //上线
            technician.setIsOnline(NumberConstants.ONE_CONSTANTS);
            technician.setOnlineTime(new Date());

            TechnicianOnlineTimeRedis technicianOnlineTimeRedisVo = JSONObject.parseObject(redisUtil.hget(RedisConstants.TECHNICIAN_ONLINE_TIME, technician.getId().toString()), TechnicianOnlineTimeRedis.class);
            if (!CheckEmptyUtil.isEmpty(technicianOnlineTimeRedisVo)) {
                technicianOnlineTimeRedisVo.setLastUpdateTime(System.currentTimeMillis());
                redisUtil.hset(RedisConstants.TECHNICIAN_ONLINE_TIME, technician.getId().toString(), JSON.toJSONString(technicianOnlineTimeRedisVo));
            }

        } else {
            //下线
            technician.setIsOnline(NumberConstants.ZERO_CONSTANTS);
        }
        return ApiRestResponse.success(technicianMapper.updateById(technician));
    }

    @Override
    public ApiRestResponse<Integer> technicianLoginOut(Technician technician) {
        //下线
        technician.setIsOnline(NumberConstants.ZERO_CONSTANTS);
        return ApiRestResponse.success(technicianMapper.updateById(technician));
    }

    @Override
    public void updateTechnicianOnlineTime() {
        //查询所有在线的技师
        List<Technician> technicianList = technicianMapper.selectList(new LambdaQueryWrapper<Technician>().eq(Technician::getIsOnline, NumberConstants.ONE_CONSTANTS));
        if (!CheckEmptyUtil.isEmpty(technicianList)) {
            technicianList.forEach(item -> {
                TechnicianOnlineTimeRedis technicianOnlineTimeRedisVo = JSONObject.parseObject(redisUtil.hget(RedisConstants.TECHNICIAN_ONLINE_TIME, item.getId().toString()), TechnicianOnlineTimeRedis.class);
                if (CheckEmptyUtil.isEmpty(technicianOnlineTimeRedisVo)) {
                    long l = DateUtil.betweenMs(item.getOnlineTime(), new Date());
                    TechnicianOnlineTimeRedis vo = new TechnicianOnlineTimeRedis();
                    vo.setTid(item.getId());
                    vo.setLastOnlineTime(l);
                    vo.setTotalOnlineTime(l);
                    vo.setLastOnlineTime(0L);
                    vo.setLastUpdateTime(System.currentTimeMillis());
                    redisUtil.hset(RedisConstants.TECHNICIAN_ONLINE_TIME, item.getId().toString(), JSON.toJSONString(vo));
                } else {
                    long l = DateUtil.betweenMs(DateUtil.date(technicianOnlineTimeRedisVo.getLastUpdateTime()), new Date());
                    technicianOnlineTimeRedisVo.setLastOnlineTime(l);
                    technicianOnlineTimeRedisVo.setTotalOnlineTime(technicianOnlineTimeRedisVo.getTotalOnlineTime() + l);
                    technicianOnlineTimeRedisVo.setLastOnlineTime(0L);
                    technicianOnlineTimeRedisVo.setLastUpdateTime(System.currentTimeMillis());
                    redisUtil.hset(RedisConstants.TECHNICIAN_ONLINE_TIME, item.getId().toString(), JSON.toJSONString(technicianOnlineTimeRedisVo));
                }

            });
        }
    }

    @Override
    public ApiRestResponse<Void> technicianUpdateTelVerify(Technician technician, TechnicianUpdateTelVo updateVo) {
        userService.checkCode(updateVo.getTel(), updateVo.getCode(), CommonConstants.TECHNICIAN_REPLACE_TEL_VERIFY);
        return ApiRestResponse.success();
    }

    @Override
    public ApiRestResponse<Integer> technicianUpdateTel(Technician technician, TechnicianUpdateTelVo updateVo) {
        userService.checkCode(updateVo.getTel(), updateVo.getCode(), CommonConstants.TECHNICIAN_REPLACE_TEL);
        technician.setTechnicianTel(updateVo.getTel());
        int update = technicianMapper.updateById(technician);

        return ApiRestResponse.success(update);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ApiRestResponse<Integer> technicianUpdate(Technician technician, TechnicianUpdateVo updateVo) {
        if (!CheckEmptyUtil.isEmpty(updateVo.getNewTechnicianAvatar())) {
            //查询是否有正在审核的记录
            int value = technicianAvatarAuditMapper.selectCount(new LambdaQueryWrapper<TechnicianAvatarAudit>()
                    .eq(TechnicianAvatarAudit::getTid, technician.getId())
                    .eq(TechnicianAvatarAudit::getAuditStatus, NumberConstants.ZERO_CONSTANTS)).intValue();
            if (value > 0) {
                throw new ErrorCodeException(ErrorCodeExceptionEnum.AVATAR_AUDITING);
            }
            TechnicianAvatarAudit technicianAvatarAudit = new TechnicianAvatarAudit();
            technicianAvatarAudit.setTid(technician.getId());
            technicianAvatarAudit.setOldAvatar(technician.getTechnicianAvatar());
            technicianAvatarAudit.setNewAvatar(updateVo.getNewTechnicianAvatar());
            technicianAvatarAuditMapper.insert(technicianAvatarAudit);
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getServiceCount())) {
            technician.setOrderReceivingTime(updateVo.getServiceCount());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getServiceDistance())) {
            technician.setServiceDistance(BigDecimal.valueOf(updateVo.getServiceDistance()));
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getTechnicianSex())) {
            technician.setTechnicianSex(updateVo.getTechnicianSex());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getTechnicianEthnic())) {
            technician.setTechnicianEthnic(updateVo.getTechnicianEthnic());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getTechnicianIntro())) {
            technician.setTechnicianIntro(updateVo.getTechnicianIntro());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getTechnicianHeight())) {
            technician.setTechnicianHeight(updateVo.getTechnicianHeight());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getTechnicianWeight())) {
            technician.setTechnicianWeight(updateVo.getTechnicianWeight());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getLongitude()) && !CheckEmptyUtil.isEmpty(updateVo.getLatitude())) {
            String reGeoCode = gaoDeUtil.ReGeoCode(updateVo.getLongitude() + "," + updateVo.getLatitude());
            WSSsdrAddress wsSsdrAddress = AddressUtil.addressFormat(reGeoCode);
            technician.setServiceCity(!CheckEmptyUtil.isEmpty(wsSsdrAddress.getCity()) ? wsSsdrAddress.getCity() : wsSsdrAddress.getProvince());
            technician.setLongitude(updateVo.getLongitude());
            technician.setLatitude(updateVo.getLatitude());
            technician.setCurrentLocation(reGeoCode);
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getDayFreeTolls())) {
            technician.setDayFreeTolls(updateVo.getDayFreeTolls());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getNightFreeTolls())) {
            technician.setNightFreeTolls(updateVo.getNightFreeTolls());
        }
        if (!CheckEmptyUtil.isEmpty(updateVo.getNewLifePhoto())) {
            //查询是否有正在审核的记录
            int value = technicianLifePhotoAuditMapper.selectCount(new LambdaQueryWrapper<TechnicianLifePhotoAudit>()
                    .eq(TechnicianLifePhotoAudit::getTid, technician.getId())
                    .eq(TechnicianLifePhotoAudit::getAuditStatus, NumberConstants.ZERO_CONSTANTS)).intValue();
            if (value > 0) {
                throw new ErrorCodeException(ErrorCodeExceptionEnum.LIFE_PHOTO_AUDITING);
            }
            TechnicianLifePhotoAudit technicianLifePhotoAudit = new TechnicianLifePhotoAudit();
            technicianLifePhotoAudit.setTid(technician.getId());
            technicianLifePhotoAudit.setNewLifePhoto(updateVo.getNewLifePhoto());
            technicianLifePhotoAudit.setOldLifePhoto(technician.getLifePhoto());
            technicianLifePhotoAuditMapper.insert(technicianLifePhotoAudit);
        }

        return ApiRestResponse.success(technicianMapper.updateById(technician));
    }


    @Override
    public ApiRestResponse<SelectTechnicianListResponseBaseVo> selectTechnicianList(Long userId, SelectTechnicianListVo requestVo) {
        String redisKey = "selectTechnicianList_technician_" + userId;
        String type = redisUtil.get(redisKey);
        if (CheckEmptyUtil.isEmpty(type)) {
            String address = gaoDeUtil.ReGeoCode(requestVo.getLongitude() + "," + requestVo.getLatitude());
            WSSsdrAddress wsSsdrAddress = AddressUtil.addressFormat(address);
            requestVo.setServiceCity(!CheckEmptyUtil.isEmpty(wsSsdrAddress.getCity()) ? wsSsdrAddress.getCity() : wsSsdrAddress.getProvince());
            redisUtil.set(redisKey, "0");
        }
        SelectTechnicianListResponseBaseVo baseVo = new SelectTechnicianListResponseBaseVo();
        //查询该用户被哪些技师屏蔽
        List<Long> shieldTids = technicianShieldMapper.selectListTids(userId);
        SelectTechnicianListVo technicianShieldVo = new SelectTechnicianListVo();
        if (!CheckEmptyUtil.isEmpty(shieldTids)) {
            technicianShieldVo.setTids(shieldTids);
            technicianShieldVo.setLongitude(requestVo.getLongitude());
            technicianShieldVo.setLatitude(requestVo.getLatitude());
            technicianShieldVo.setUserId(userId);
        }


        List<Technician> technicianShieldList;

        List<Technician> technicianList;
        requestVo.setUserId(userId);

        PageHelper.startPage(requestVo.getPageNum(), requestVo.getPageSize());
        if (!"-1".equals(requestVo.getDay()) && !"-1".equals(requestVo.getTime())) {
            technicianList = technicianMapper.selectTechnicianListByDay(requestVo);

            technicianShieldList = technicianMapper.selectTechnicianListByDay(technicianShieldVo);
        } else {
            technicianList = technicianMapper.selectTechnicianList(requestVo);
            technicianShieldList = technicianMapper.selectTechnicianList(technicianShieldVo);
        }

        List<SelectTechnicianListResponseVo> voList = new ArrayList<>();
        if (!CheckEmptyUtil.isEmpty(technicianList)) {
            // 使用Stream API过滤list1，去除在list2中出现的Person对象
            List<Technician> result = technicianList.stream()
                    .filter(item -> technicianShieldList.stream().noneMatch(p -> p.getId().equals(item.getId())))
                    .collect(Collectors.toList());
            if (CheckEmptyUtil.isEmpty(result)) {
                baseVo.setTechnicianList(new PageInfo<>(voList));
            } else {
                PageInfo<Technician> source = new PageInfo<>(result);
                PageInfo<SelectTechnicianListResponseVo> target = new PageInfo<>();
                BeanUtils.copyProperties(source, target);
                source.getList().forEach(item -> {
                    SelectTechnicianListResponseVo vo = new SelectTechnicianListResponseVo();
                    vo.setId(item.getId());
                    vo.setTechnicianAvatar(item.getTechnicianAvatar());
                    vo.setTechnicianName(item.getTechnicianName());

                    vo.setTechnicianAge(selectTechnicianAge(item));
                    vo.setTechnicianSex(item.getTechnicianSex());
                    vo.setMark(item.getMark());
                    vo.setOrderReceiving(selectTechnicianOrderReceiving(item) + item.getOrderReceiving());
                    vo.setOrderReceivingTime(item.getOrderReceivingTime());
                    String distance = MathDistanceUtil.getDistance(requestVo.getLongitude() + "," + requestVo.getLatitude(), item.getLongitude() + "," + item.getLatitude(), 2);
                    BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
                    vo.setDistance(distanceBig.toPlainString());
                    Merchant merchant = merchantMapper.selectById(item.getMerchantId());
                    vo.setMerchantName(CheckEmptyUtil.isEmpty(merchant) ? "" : merchant.getMerchantName());
                    int technicianAppraiseCount = userAppraiseMapper.selectCount(new LambdaQueryWrapper<UserAppraise>()
                            .eq(UserAppraise::getTid, item.getId())).intValue();
                    vo.setCommentCount(technicianAppraiseCount);

                    int technicianAttentionCount = userAttentionMapper.selectCount(new LambdaQueryWrapper<UserAttention>()
                            .eq(UserAttention::getTid, item.getId())).intValue();
                    vo.setAttentionCount(technicianAttentionCount);
                    vo.setEarliestTime(selectTechnicianEarliesTime(item.getId()));
                    TechnicianType technicianType = technicianTypeMapper.selectById(item.getTechnicianType());
                    vo.setTechnicianType(technicianType.getTypeName());
                    vo.setTypePicture(technicianType.getTypePicture());
                    try {
                        vo.setIsFreeCar(isFreeCar(requestVo.getLongitude() + "," + requestVo.getLatitude(), item));
                    } catch (ParseException e) {
                        throw new RuntimeException(e);
                    }
                    voList.add(vo);
                });
                target.setList(voList);
                baseVo.setTechnicianList(target);
            }

        } else {

            baseVo.setTechnicianList(new PageInfo<>(voList));
        }
        //String address = gaoDeUtil.GaoGetTownship(requestVo.getLongitude() + "," + requestVo.getLatitude());
        //baseVo.setPositAddress(address);

        return ApiRestResponse.success(baseVo);
    }


    private Integer isFreeCar(String userLongLat, Technician technician) throws ParseException {
        if (CheckEmptyUtil.isEmpty(userLongLat)) {
            return NumberConstants.ZERO_CONSTANTS;
        }
        //单位：米
        BigDecimal realDistance = new BigDecimal(MathDistanceUtil.getDistance(userLongLat, technician.getLongitude() + "," + technician.getLatitude(), 2));

        String userAddress = gaoDeUtil.ReGeoCode(userLongLat);
        WSSsdrAddress wsSsdrAddress = AddressUtil.addressFormat(userAddress);
        //城市
        String city = wsSsdrAddress.getCity();
        //省份
        String province = wsSsdrAddress.getProvince();
        //日间开始时间
        String dayStartTime;
        //日间结束时间
        String dayEndTime;

        //获取城市路费设置
        CityFareConfig cityFareConfig = cityFareConfigMapper.selectOneCity(city, province);
        if (CheckEmptyUtil.isEmpty(cityFareConfig)) {
            //获取全局配置
            CityFareGlobalConfig dayStartTimeGlobal = cityFareGlobalConfigMapper.selectOne(new LambdaQueryWrapper<CityFareGlobalConfig>().select(CityFareGlobalConfig::getValue).eq(CityFareGlobalConfig::getCode, "day_start_time"));
            CityFareGlobalConfig dayEndTimeGlobal = cityFareGlobalConfigMapper.selectOne(new LambdaQueryWrapper<CityFareGlobalConfig>().select(CityFareGlobalConfig::getValue).eq(CityFareGlobalConfig::getCode, "day_end_time"));
            dayStartTime = dayStartTimeGlobal.getValue();
            dayEndTime = dayEndTimeGlobal.getValue();
        } else {
            dayStartTime = cityFareConfig.getDayStartTime();
            dayEndTime = cityFareConfig.getDayEndTime();
        }
        BigDecimal distance;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
        boolean in = DateUtil.isIn(new Date(), simpleDateFormat.parse(dayStartTime), simpleDateFormat.parse(dayEndTime));
        if (in) {
            //白天时间
            log.info("当前为白天时间段");
            distance = technician.getDayFreeTolls().multiply(new BigDecimal(1000));


        } else {
            //晚上时间
            log.info("当前为晚上时间段");
            distance = technician.getNightFreeTolls().multiply(new BigDecimal(1000));
        }
        if (realDistance.compareTo(distance) <= 0) {
            return NumberConstants.ONE_CONSTANTS;
        } else {
            return NumberConstants.ZERO_CONSTANTS;
        }
    }

    @Override
    public ApiRestResponse<SelectTechnicianDetailResponseVo> selectTechnicianDetail(Long userId, SelectTechnicianListVo requestVo) {
        SelectTechnicianDetailResponseVo vo = new SelectTechnicianDetailResponseVo();
        Technician technician = technicianMapper.selectById(requestVo.getTid());

        vo.setId(technician.getId());
        vo.setOrderReceivingTime(technician.getOrderReceivingTime());
        vo.setOrderReceiving(selectTechnicianOrderReceiving(technician) + technician.getOrderReceiving());
        vo.setTechnicianAvatar(technician.getTechnicianAvatar());
        vo.setTechnicianName(technician.getTechnicianName());
        vo.setTechnicianAge(technician.getTechnicianAge());
        vo.setTechnicianSex(technician.getTechnicianSex());
        vo.setMark(technician.getMark());
        vo.setTechnicianHeight(technician.getTechnicianHeight());
        vo.setTechnicianWeight(technician.getTechnicianWeight());
        vo.setIsOnline(technician.getIsOnline());
        vo.setTechnicianIntro(technician.getTechnicianIntro());
        vo.setLifePhoto(technician.getLifePhoto());
        int technicianAttentionCount = userAttentionMapper.selectCount(new LambdaQueryWrapper<UserAttention>()
                .eq(UserAttention::getTid, requestVo.getTid())
                .eq(UserAttention::getUserId, userId)).intValue();
        vo.setAttentionStatus(technicianAttentionCount);
        Merchant merchant = merchantMapper.selectById(technician.getMerchantId());
        vo.setMerchantName(CheckEmptyUtil.isEmpty(merchant) ? "" : merchant.getMerchantName());
        String distance = MathDistanceUtil.getDistance(requestVo.getLongitude() + "," + requestVo.getLatitude(), technician.getLongitude() + "," + technician.getLatitude(), 2);
        BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
        vo.setDistance(distanceBig.toPlainString());
        vo.setEarliestTime(selectTechnicianEarliesTime(requestVo.getTid()));

        PageHelper.startPage(requestVo.getPageNum(), requestVo.getPageSize());
        List<UserAppraise> userAppraises = userAppraiseMapper.selectList(new LambdaQueryWrapper<UserAppraise>()
                .eq(UserAppraise::getTid, requestVo.getTid())
                .orderByDesc(UserAppraise::getCreateTime));
        List<SelectCommentListResponseVo> commonList = new ArrayList<>();
        if (!CheckEmptyUtil.isEmpty(userAppraises)) {
            PageInfo<UserAppraise> source = new PageInfo<>(userAppraises);
            PageInfo<SelectCommentListResponseVo> target = new PageInfo<>();
            BeanUtils.copyProperties(source, target);
            List<UserAppraise> sourceList = source.getList();
            //if (sourceList.size() > 3) {
            //    sourceList = sourceList.subList(0, 2);
            //}
            sourceList.forEach(item -> {
                SelectCommentListResponseVo vo1 = new SelectCommentListResponseVo();
                User user = userMapper.selectById(item.getUserId());
                vo1.setUserAvatar(user.getUserAvatar());
                vo1.setUserName(DesensitizedUtil.chineseName(user.getUserNikeName()));
                vo1.setItemName(item.getItemName());
                vo1.setCommentText(item.getCommentText());
                vo1.setSatisfaction(item.getSatisfaction());
                vo1.setCreateTime(item.getCreateTime());
                vo1.setCommentPicture(item.getCommentPicture());
                commonList.add(vo1);
            });
            target.setList(commonList);
            vo.setCommentList(target);
        } else {
            vo.setCommentList(new PageInfo<>(Collections.emptyList()));
        }
        vo.setCommentCount(commonList.size());
        vo.setItemList(PageHelper.startPage(requestVo.getPageNum(), requestVo.getPageSize()).doSelectPageInfo(() -> {
            itemMapper.selectItemListByBachTid(Arrays.asList(technician.getServiceItem().split(",")));
        }));

        int technicianAuthenticationCount = technicianAuthenticationMapper.selectCount(new LambdaQueryWrapper<TechnicianAuthentication>()
                .eq(TechnicianAuthentication::getTid, requestVo.getTid())).intValue();
        vo.setAptitudeCount(NumberChineseFormatter.format(technicianAuthenticationCount, false, false));
        return ApiRestResponse.success(vo);
    }

    /**
     * 查询技师最早可约时间
     *
     * @param tid
     * @return
     * @Date 2025 3月 05 14:23
     */
    @Override
    public String selectTechnicianEarliesTime(Long tid) {

        SelectLatelyTime selectLatelyTime = technicianTimeMapper.selectTechnicianLatelyTime(tid, DateUtil.today().substring(DateUtil.today().indexOf("-") + 1));
        if (!CheckEmptyUtil.isEmpty(selectLatelyTime)) {
            return selectLatelyTime.getDay() + " " + selectLatelyTime.getTime();
        } else {
            String torm = DateUtil.format(DateUtil.offsetDay(new Date(), 1), "yyyy-MM-dd");
            SelectLatelyTime selectTormLatelyTime = technicianTimeMapper.selectTechnicianTormLatelyTime(tid, torm.substring(torm.indexOf("-") + 1));
            if (!CheckEmptyUtil.isEmpty(selectTormLatelyTime)) {
                return selectTormLatelyTime.getDay() + " " + selectTormLatelyTime.getTime();
            } else {
                return "暂无时间";
            }
        }


        //TechnicianTime technicianTime = technicianTimeMapper.selectOne(new LambdaQueryWrapper<TechnicianTime>()
        //        .eq(TechnicianTime::getTechnicianId, tid)
        //        .eq(TechnicianTime::getStatus, NumberConstants.ZERO_CONSTANTS)
        //        .orderByAsc(TechnicianTime::getTime)
        //        .ge(TechnicianTime::getTime, new SimpleDateFormat("HH:mm").format(new Date()))
        //        .last("limit 1;"));
        //if (CheckEmptyUtil.isEmpty(technicianTime)) {
        //    return "暂无时间";
        //} else {
        //    return technicianTime.getDay() + " " + technicianTime.getTime();
        //}
    }

    /**
     * 根据技师年龄 判断技师属于00后还是其他
     *
     * @param technician
     * @return
     * @Date 2025 3月 05 14:39
     */
    @Override
    public String selectTechnicianAge(Technician technician) {
        int year = technician.getTechnicianYear();
        if (year <= 1974) {
            return "70后";
        } else if (year < 1979 && year >= 1975) {
            return "75后";
        } else if (year < 1984 && year >= 1979) {
            return "80后";
        } else if (year < 1989 && year >= 1984) {
            return "85后";
        } else if (year < 1995 && year >= 1989) {
            return "90后";
        } else if (year < 1999 && year >= 1995) {
            return "95后";
        } else if (year < 2005 && year >= 1999) {
            return "00后";
        } else if (year < 2009 && year >= 2005) {
            return "05后";
        } else {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.TECHNICIAN_AGE_ERROR);
        }
    }

    /**
     * 查技师接单量(普通+加钟+升级)
     *
     * @param technician
     * @return
     * @Date 2025 3月 05 14:43
     */
    @Override
    public Integer selectTechnicianOrderReceiving(Technician technician) {
        Integer orderReceivingTime = technician.getOrderReceivingTime();
        Long l = technicianItemOrderMapper.selectCount(new LambdaQueryWrapper<TechnicianItemOrder>()
                .eq(TechnicianItemOrder::getTid, technician.getId())
                .notIn(TechnicianItemOrder::getOrderStatus, NumberConstants.ZERO_CONSTANTS, NumberConstants.TEN_CONSTANTS));
        if (l <= 0) {
            return NumberConstants.ZERO_CONSTANTS;
        } else {
            if (orderReceivingTime == 0) {
                return technicianItemOrderMapper.selectCount(new LambdaQueryWrapper<TechnicianItemOrder>()
                        .eq(TechnicianItemOrder::getTid, technician.getId())
                        .notIn(TechnicianItemOrder::getOrderStatus, NumberConstants.ZERO_CONSTANTS, NumberConstants.TEN_CONSTANTS)).intValue();
            } else {
                return technicianItemOrderMapper.selectCount(new LambdaQueryWrapper<TechnicianItemOrder>()
                        .eq(TechnicianItemOrder::getTid, technician.getId())
                        .ge(TechnicianItemOrder::getPayTime, DateUtil.offsetDay(new Date(), -orderReceivingTime))
                        .notIn(TechnicianItemOrder::getOrderStatus, NumberConstants.ZERO_CONSTANTS, NumberConstants.TEN_CONSTANTS)).intValue();
            }

        }
    }

    @Override
    public ApiRestResponse<SelectTechnicianListResponseBaseVo> selectTechnicianListByItemId(Long userId, SelectTechnicianListVo requestVo) {
        SelectTechnicianListResponseBaseVo baseVo = new SelectTechnicianListResponseBaseVo();
        String redisKey = "selectTechnicianList_technician_" + userId;
        String type = redisUtil.get(redisKey);
        if (CheckEmptyUtil.isEmpty(type)) {
            String address = gaoDeUtil.ReGeoCode(requestVo.getLongitude() + "," + requestVo.getLatitude());
            WSSsdrAddress wsSsdrAddress = AddressUtil.addressFormat(address);
            requestVo.setServiceCity(!CheckEmptyUtil.isEmpty(wsSsdrAddress.getCity()) ? wsSsdrAddress.getCity() : wsSsdrAddress.getProvince());
            redisUtil.set(redisKey, "0");
        }


        //if (requestVo.getType().equals(NumberConstants.ONE_CONSTANTS)) {
        //    String address = gaoDeUtil.ReGeoCode(requestVo.getLongitude() + "," + requestVo.getLatitude());
        //    WSSsdrAddress wsSsdrAddress = AddressUtil.addressFormat(address);
        //    requestVo.setServiceCity(!CheckEmptyUtil.isEmpty(wsSsdrAddress.getCity()) ? wsSsdrAddress.getCity() : wsSsdrAddress.getProvince());
        //
        //}
        Item items = itemMapper.selectById(requestVo.getItemId());
        if (CheckEmptyUtil.isEmpty(items)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ITEM_EMPTY);
        }
        List<Long> tids = Arrays.stream(items.getTid().split(","))
                .map(Long::parseLong)
                .collect(Collectors.toList());
        List<Long> shieldTids = technicianShieldMapper.selectListTids(userId);

        // 使用Stream API去除list1中包含在list2中的元素
        List<Long> result = tids.stream()
                .filter(item -> !shieldTids.contains(item))
                .collect(Collectors.toList());


        requestVo.setTids(result);
        requestVo.setServerStatus(NumberConstants.ONE_CONSTANTS);
        PageHelper.startPage(requestVo.getPageNum(), requestVo.getPageSize());

        requestVo.setUserId(userId);
        List<Technician> technicianList = technicianMapper.selectTechnicianList(requestVo);
        List<SelectTechnicianListResponseVo> voList = new ArrayList<>();
        if (!CheckEmptyUtil.isEmpty(technicianList)) {
            PageInfo<Technician> source = new PageInfo<>(technicianList);
            PageInfo<SelectTechnicianListResponseVo> target = new PageInfo<>();
            BeanUtils.copyProperties(source, target);
            source.getList().forEach(item -> {
                SelectTechnicianListResponseVo vo = new SelectTechnicianListResponseVo();
                vo.setId(item.getId());
                vo.setTechnicianAvatar(item.getTechnicianAvatar());
                vo.setTechnicianName(item.getTechnicianName());
                vo.setTechnicianAge(selectTechnicianAge(item));
                vo.setTechnicianSex(item.getTechnicianSex());
                vo.setMark(item.getMark());
                vo.setOrderReceiving(selectTechnicianOrderReceiving(item) + item.getOrderReceiving());
                vo.setOrderReceivingTime(item.getOrderReceivingTime());
                String distance = MathDistanceUtil.getDistance(requestVo.getLongitude() + "," + requestVo.getLatitude(), item.getLongitude() + "," + item.getLatitude(), 2);
                BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
                vo.setDistance(distanceBig.toPlainString());
                Merchant merchant = merchantMapper.selectById(item.getMerchantId());
                vo.setMerchantName(CheckEmptyUtil.isEmpty(merchant) ? "" : merchant.getMerchantName());
                int technicianAppraiseCount = userAppraiseMapper.selectCount(new LambdaQueryWrapper<UserAppraise>()
                        .eq(UserAppraise::getTid, item.getId())).intValue();
                vo.setCommentCount(technicianAppraiseCount);

                int technicianAttentionCount = userAttentionMapper.selectCount(new LambdaQueryWrapper<UserAttention>()
                        .eq(UserAttention::getTid, item.getId())).intValue();
                vo.setAttentionCount(technicianAttentionCount);
                vo.setEarliestTime(selectTechnicianEarliesTime(item.getId()));
                TechnicianType technicianType = technicianTypeMapper.selectById(item.getTechnicianType());
                vo.setTechnicianType(technicianType.getTypeName());
                vo.setTypePicture(technicianType.getTypePicture());
                try {
                    vo.setIsFreeCar(isFreeCar(requestVo.getLongitude() + "," + requestVo.getLatitude(), item));
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
                voList.add(vo);
            });
            target.setList(voList);
            baseVo.setTechnicianList(target);

        } else {

            baseVo.setTechnicianList(new PageInfo<>(voList));
        }

        return ApiRestResponse.success(baseVo);
    }


}
